﻿
let MouseList: Array<Mouse> = [];
let valueDiv: HTMLDivElement;

document.onmousemove = mousemove;
document.onmouseup = mouseup;

function mousemove(e: MouseEvent) {
    if (MouseList != null && MouseList.length != 0)
        for (let m of MouseList) {
            m.onmousemove(e);
        }
}

function mouseup(e: MouseEvent) {
    if (MouseList != null && MouseList.length != 0)
        for (let m of MouseList) {
            m.onmouseup(e);
        }
}



interface Mouse {
    onmousedown(e: MouseEvent);
    onmousemove(e: MouseEvent);
    onmouseup(e: MouseEvent);
}

class DragDiv implements Mouse {
    myDiv: HTMLDivElement;
    isMouse: boolean = false;
    l: number;
    t: number;
    x: number = 0;
    y: number = 0;
    refreshPosition: any = null;
    _onmouseup: any;
    _onmousedown: any;
    constructor(div: HTMLDivElement) {
        this.myDiv = div;
        this.myDiv.style.position = "absolute";

        this.myDiv.addEventListener("mousedown", this.onmousedown.bind(this));
        MouseList.push(this);
    }

    onmousedown(e: MouseEvent) {
        this.isMouse = true
        if (e.preventDefault) {
            e.preventDefault();
        } else {
            e.returnValue = false;
        }
        if (this._onmousedown != null)
            this._onmousedown();
        this.l = parseInt(this.myDiv.style.left);
        this.t = parseInt(this.myDiv.style.top);
        this.x = e.x - this.l;
        this.y = e.y - this.t;
    }

    onmousemove(e: MouseEvent) {
        if (this.isMouse) {
            if (e.preventDefault) {
                e.preventDefault();
            } else {
                e.returnValue = false;
            }
            if (this.refreshPosition != null)
                this.refreshPosition();
            this.myDiv.style.left = (this.l = e.x - this.x) + "px";
            this.myDiv.style.top = (this.t = e.y - this.y) + "px";
        }
    }
    onmouseup(e: MouseEvent) {
        if (this.isMouse) {
            if (this._onmouseup != null) {
                this._onmouseup();
            }
        }
        this.isMouse = false;
    }
}


abstract class MyNode{
    myDiv: HTMLDivElement;
    hideDiv: HTMLDivElement;
    hideDivMain: HTMLDivElement;
    isTop: boolean = true;

    private div: HTMLDivElement;
    curve: CurvePath = null;
    constructor(div: HTMLDivElement) {
        this.myDiv = document.createElement("div");
        this.myDiv.style.position = "absolute";
        this.myDiv.style.top = "0px";
        this.myDiv.style.left = "0px";
        div.appendChild(this.myDiv);
        this.hideDiv = document.createElement("div");
        this.hideDiv.style.position = "absolute";
        $(div).after(this.hideDiv);
        this.myDiv.style.position = "absolute";
        this.hideDiv.style.top = this.myDiv.clientLeft + div.clientLeft + "px";
        this.hideDiv.style.left = this.myDiv.clientTop + div.clientTop + "px";
        this.div = div;
        this.setMyDiv(this.myDiv);

        this.hideDivMain = document.createElement("div");
        this.hideDiv.appendChild(this.hideDivMain);
        this.sethideDiv(this.hideDivMain);
        this.hideDiv.style.height = this.hideDivMain.style.height;
        this.hideDiv.style.width = this.hideDivMain.style.width;
        this.hideDivMain.hidden = true;

        this.hideDiv.addEventListener("mousemove", () => {
            this.hideDivMain.hidden = false;
        });

        this.hideDiv.addEventListener("mouseout", () => {
            this.hideDivMain.hidden = true;
        });
        let hideDrag = new DragDiv(this.hideDiv);

        hideDrag.refreshPosition = () => {
            if (this.curve != null && !this.curve.Iv) {
                if ((this.isTop && !this.curve.Bd) || (!this.isTop && this.curve.Bd)) {
                    this.curve.Ex = parseInt(this.hideDiv.style.left);
                    this.curve.Ey = parseInt(this.hideDiv.style.top);
                } else {
                    this.curve.Sx = parseInt(this.hideDiv.style.left);
                    this.curve.Sy = parseInt(this.hideDiv.style.top);
                }
                curve.draw(this.curve);
            }
        }

        this.hideDiv.addEventListener("mousedown", () => {
            if (this.curve == null || this.curve.Iv) {
                console.log("创建路径");
                this.isTop = true;
                this.newCuvePath();
            }
        });

        hideDrag._onmouseup = () => {
            if (this.curve != null) {
                if (curve.connect(this.curve, this.isTop)) {
                    this.refreshPosition();
                } else {
                    curve.clear(this.curve);
                    this.refreshPosition();
                    this.isTop = true;
                    this.curve = null;
                }

            }
        }

        hideDrag._onmousedown = () => {
            $(valueDiv).prepend(this.div);
            $(div).after(this.hideDiv);
        }

       
        curve.addConnect((curvePath: CurvePath, isTop: boolean) => {
            if (this.curve == null || this.curve.Iv) {
                if (!this.hideDivMain.hidden) {
                    this.isTop = !isTop;
                    this.curve = curvePath;
                    this.curve.Bd = true;
                    this.connectCuve()
                    return true
                }
            }
            return false;
        });

    }

    newCuvePath() {
        this.curve = curve.NewCurve(parseInt(this.hideDiv.style.left), parseInt(this.hideDiv.style.top));
        this.NewCuve();
    }

    refreshPosition() {
        this.hideDiv.style.top = parseInt(this.myDiv.style.top) + parseInt(this.div.style.top) + "px";
        this.hideDiv.style.left = parseInt(this.myDiv.style.left) + parseInt(this.div.style.left) + "px";
        if (this.curve != null) {
            if (this.isTop) {
                this.curve.Sx = parseInt(this.hideDiv.style.left);
                this.curve.Sy = parseInt(this.hideDiv.style.top);
            } else {
                this.curve.Ex = parseInt(this.hideDiv.style.left);
                this.curve.Ey = parseInt(this.hideDiv.style.top);
            }
            curve.draw(this.curve);
        }
    }

    abstract setMyDiv(div: HTMLDivElement);
    abstract sethideDiv(div: HTMLDivElement);
    abstract NewCuve();
    abstract connectCuve();

}


class Curve {
    div: HTMLElement;
    svg: SVGSVGElement;
    curvePathList: [(curvePath: CurvePath, isTop: boolean) => boolean];

    constructor(div: HTMLElement) {
        this.div = div;
        this.curvePathList = [] as [(curvePath: CurvePath) => boolean ];
        this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        this.svg.style.position = "absoulte";
        this.svg.setAttribute("width", window.outerWidth + "");
        this.svg.setAttribute("height", window.outerHeight + "");
        div.appendChild(this.svg);
    }

    NewCurve(SX: number, SY: number, SDi: Direction = Direction.Right, color: string = "blue", width: string = "5"): CurvePath {
        let path: SVGPathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttribute("fill", "none");
        this.svg.appendChild(path);
        let Cp = new CurvePath();
        Cp.Path = path;
        Cp.Sx = SX;
        Cp.Sy = SY;
        Cp.Ex = SX;
        Cp.Ey = SY;
        Cp.Co = color;
        Cp.Wi = width;
        Cp.Sd = SDi;
        this.draw(Cp);
        return Cp;
    }

    addConnect(connect: (curvePath: CurvePath, isTop: boolean) => boolean ) {
        this.curvePathList.push(connect);
    }

    connect(curvePath: CurvePath, isTop: boolean): boolean {
        let b = false;
        for (let conn of this.curvePathList) {
            if (conn(curvePath, isTop)) {
                b = true;
                continue;
            }
        }
        return b;
    }

    
    clear(curvePath: CurvePath) {
        curvePath.Iv = true;
        this.svg.removeChild(curvePath.Path);
    }

    draw(curvePath: CurvePath) {
        curvePath.Path.setAttribute("stroke", curvePath.Co);
        curvePath.Path.setAttribute("stroke-width", curvePath.Wi);
        let SMx = 0;
        let SMy = 0;
        let EMx = (curvePath.Ex - curvePath.Sx);
        let EMy = (curvePath.Ey - curvePath.Sy);

        switch (curvePath.Sd) {
            case Direction.Top:
                SMy -= curvePath.Sl;
                break;
            case Direction.Right:
                SMx += curvePath.Sl;
                break;
            case Direction.Bottom:
                SMy += curvePath.Sl;
                break;
            case Direction.Left:
                SMx -= curvePath.Sl;
                break;
        }

        switch (curvePath.Ed) {
            case Direction.Top:
                EMy -= curvePath.El;
                break;
            case Direction.Right:
                EMx += curvePath.El;
                break;
            case Direction.Bottom:
                EMy += curvePath.El;
                break;
            case Direction.Left:
                EMx  -= curvePath.El;
                break;
        }

        curvePath.Path.setAttribute("d", "M " + curvePath.Sx + " " + curvePath.Sy + " c " + SMx + " " + SMy + " " + EMx + " " + EMy + " " + (curvePath.Ex - curvePath.Sx) + " " + (curvePath.Ey - curvePath.Sy));
    }

}

class CurvePath {
    Path: SVGPathElement;
    Sx: number = 0;//起始x点
    Sy: number = 0;//起始y点
    Ex: number = 0;//结束x点
    Ey: number = 0;//结束y点
    Sd: Direction = Direction.Right;//起始方向
    Ed: Direction = Direction.Right;//结束方向
    Sl: number = 0;//起始瞄点距离
    El: number = 0;//结束瞄点距离

    Co: string = "blue"; //颜色
    Wi: string = "5"; //宽度
    Iv: boolean = false; //是否已清楚
    Bd: boolean = false;//是否已双向绑定
}

enum Direction {
    Top,
    Left,
    Bottom,
    Right,
}

